package see.battle.object;

import java.util.ArrayList;
import java.util.concurrent.Callable;

import see.battle.Const;
import see.battle.LifeCycle.BulletProcessor;
import see.battle.drawer.Drawer;

public class ObjectPool {
  private Drawer _drawer;
  private int _gameMode;
  private Background _bg;
  private Background _redbg;
  private Background _graybg;
  private Background _blastbg;
  private Ocular _oc;
  private OpponentFocus _opFocus;
  private Fire _fire;
  private Fire _gfire;
  private ShipContainer _ships;
  private ArrayList<Bullet> _bullets = new ArrayList<Bullet>();
  private int _bulletNumber = Const.maxBulletNumber;
  private int _hitNumber = 0;
  private int _opponentBulletNumber = Const.maxBulletNumber;
  private int _opponentHitNumber = 0;
  private int _bgCounter;
  private boolean _inBlast = false;
  private boolean _nextHitIsMine = true;
  private BulletProcessor _bulletProcesor;
  
  public class FireProcessor implements Callable<Object> {
    float _x;
    float _y;
    
    public void setX(float x) {
      _x = x;
    }

    public void setY(float y) {
      _y = y;
    }

    //@Override
    public Object call() throws Exception {
      tryToSendBullet(_x, _y);
      return null;
    }
  }
  
  public class MoveOcularProcessor implements Callable<Object> {
    float _dx;

    public void setDx(float dx) {
      _dx = dx;
    }

    //@Override
    public Object call() throws Exception {
      moveOcular(_dx);
      return null;
    }
  }
  
  public class DrawProcessor implements Callable<Object> {
    
    //@Override
    public Object call() throws Exception {
      draw();
      return null;
    }    
  }

  public ObjectPool(Drawer drawer, int gameMode) {
    _drawer = drawer;
    _gameMode = gameMode;
    _drawer.set_fireProcessor(new FireProcessor());
    _drawer.set_moveOcularProcessor(new MoveOcularProcessor());
    _drawer.set_drawProcessor(new DrawProcessor());
    if (_gameMode == Const.noServer) {
      _opponentHitNumber  = -1;
    }
    if (_drawer != null) {
      _bg = new Background(_drawer, Const.normalBackgroundType);
      _redbg = new Background(_drawer, Const.redBackgroundType);
      _graybg = new Background(_drawer, Const.grayBackgroundType);
      _blastbg = _redbg;
      _oc = new Ocular(_drawer);
      _opFocus = new OpponentFocus(_drawer, 0, _bg);
      _fire = new Fire(_drawer, Const.activeFireType);
      _gfire = new Fire(_drawer, Const.inactiveFireType);
    }
    _ships = new ShipContainer(Const.fromLeftToRightDirection);
  }

  public void set_bulletProcesor(BulletProcessor bulletProcesor) {
    _bulletProcesor = bulletProcesor;
  }

  public int get_hitNumber() {
    return _hitNumber;
  }

  public int get_opponentHitNumber() {
    return _opponentHitNumber;
  }

  public void increaseHitsNumber() {
    if (_nextHitIsMine) {
      _hitNumber += 1;  
    } else {
      _opponentHitNumber += 1;
    }
  }
  
  public void set_nextHitIsMine(boolean nextHitIsMine) {
    _nextHitIsMine = nextHitIsMine;
  }

  public boolean is_inBlast() {
    return _inBlast;
  }

  public void set_inBlast(boolean inBlast) {
    _inBlast = inBlast;
  }
  
  public boolean noMoreBullets() {
    return ((_bulletNumber == 0) && (_gameMode == Const.noServer)) ||
        ((_opponentBulletNumber == 0) || (_bulletNumber == 0));
  }
  
  public void initShipsRandom(long seed) {
    _ships.initRandom(seed);
  }

  public long proceedBullet(float position) {
    synchronized (_ships) {
      return _ships.proceedBullet(position);
    }
  }
  
  public void changeShipsDirection() {
    synchronized (_ships) {
      _ships.changeDirection();
    }
  }
  
  public void addShip(long creationTime, long currentTime) {
    synchronized (_ships) {
      _ships.addShip(_drawer, _bg, creationTime, currentTime);
    }
  }
  
  public void stopShipsForBlast(){
    synchronized (_ships) {
      _ships.stopShipsForBlast(); 
    }
  }
    
  public void update(long currentTime) {
    if (!_inBlast) {
      synchronized (_ships) {
        _ships.updateShips(currentTime);
      }
    }
    synchronized (_bullets) {
      for (Bullet e : _bullets) {
        e.update(currentTime);
      }
    }
  }

  public void resetBlastBackground() {
    switch ((_bgCounter / Const.blastBackgroundChangeDelayInSteps) 
        % Const.blastBackgroundNumber) {
      case 0: _blastbg = _redbg; break;
      case 1: _blastbg = _graybg; break;
    }
    _bgCounter++;
  }
  
  public void drawScene() {
    _drawer.drawScene();
  }
  
  public void draw() {
    if (!_inBlast) {
      _bg.draw();
      synchronized (_ships) {
        _ships.drawShips();
      }
      synchronized (_bullets) {
        for (Bullet e : _bullets) {
          e.draw();
        }
      }
      if (_gameMode != Const.noServer) {
        _opFocus.draw();
      }
      _oc.draw();
      _fire.draw();
      _drawer.drawCurrentScore(_hitNumber, _bulletNumber, 
          _opponentHitNumber, _opponentBulletNumber);      
    } else {
      _blastbg.draw(); 
      _oc.draw();
      _gfire.draw();
      _drawer.drawCurrentScore(_hitNumber, _bulletNumber, 
          _opponentHitNumber, _opponentBulletNumber);      
    }
  }

  public void addBullet(float x, long creationTime, boolean bulletIsMine, 
      long currentTime) {
    if (bulletIsMine) {
      _bulletNumber -= 1;
    } else {
      _opponentBulletNumber -= 1;
    }
    synchronized (_bullets) {
      if (bulletIsMine) {
        _bullets.add(new Bullet(_drawer, Const.myBulletType, x, _bg, 
            creationTime, currentTime));
      } else {
        _bullets.add(new Bullet(_drawer, Const.opponentBulletType, x, _bg, 
            creationTime, currentTime));
      }
    }
    _bgCounter = 0;
  }
  
  public float getCurrentFocus() {
    return Const.screenMiddle - _bg.get_position();
  }
  
  public void changeOpponentFocus(float focus) {
    _opFocus.moveTo(focus);
  }
  
  public void tryToSendBullet(float x, float y) throws Exception {
    if (!_inBlast && (_bulletNumber != 0)) {
      if (x < Const.fireButtonRightLimit && 
          y < Const.fireButtonBottomLimit) {
        float focus = (float) (Const.screenMiddle - _bg.get_position());
        long additionalShipDelay = proceedBullet(focus); 
        if (_bulletProcesor != null) {
          _bulletProcesor.setTime(System.currentTimeMillis());
          _bulletProcesor.setPosition(focus);
          _bulletProcesor.setBulletIsMine(true);
          _bulletProcesor.setAdditionalShipDelay(additionalShipDelay);
          _bulletProcesor.call();
        }
        if (_bulletNumber == 0) {
          _fire = _gfire;
        }
      }
    }
  }

  public void moveOcular(float dx) {
    float position = _bg.get_position() + dx;
    _bg.moveTo(position);
    _redbg.moveTo(position);
    _graybg.moveTo(position);
  }
  
}
